{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# 5. Backend 系统（后端）\n",
        "\n",
        "## 知识点讲解\n",
        "\n",
        "Backend 是 Matplotlib 的渲染引擎，负责将图形对象转换为实际的图像输出。\n",
        "\n",
        "### 什么是 Backend\n",
        "\n",
        "**定义**：\n",
        "- Backend 是 Matplotlib 的底层渲染系统\n",
        "- 负责处理图形在不同平台和输出格式上的渲染\n",
        "- 是 Matplotlib 三层架构中的最底层\n",
        "\n",
        "**作用**：\n",
        "- 将抽象的图形对象转换为像素图像\n",
        "- 处理不同平台的图形显示\n",
        "- 支持多种输出格式（PNG、PDF、SVG 等）\n",
        "\n",
        "### 常用 Backend\n",
        "\n",
        "**交互式 Backend**（支持图形窗口交互）：\n",
        "- `TkAgg`: 基于 Tkinter，跨平台，默认选择\n",
        "- `Qt5Agg`: 基于 Qt5，功能强大，需要安装 PyQt5\n",
        "- `Qt4Agg`: 基于 Qt4，较旧版本\n",
        "- `GTK3Agg`: 基于 GTK3，主要用于 Linux\n",
        "\n",
        "**非交互式 Backend**（用于文件输出）：\n",
        "- `Agg`: 基于 Anti-Grain Geometry，高质量光栅图形\n",
        "- `PDF`: 输出 PDF 格式\n",
        "- `SVG`: 输出 SVG 格式\n",
        "- `PS`: 输出 PostScript 格式\n",
        "\n",
        "**Jupyter 专用 Backend**：\n",
        "- `inline`: 在 Notebook 中内联显示（静态）\n",
        "- `notebook`: 交互式显示（已弃用）\n",
        "- `widget`: 使用 ipywidgets 的交互式显示\n",
        "\n",
        "### 设置 Backend\n",
        "\n",
        "**语法格式**：\n",
        "```python\n",
        "import matplotlib\n",
        "matplotlib.use('backend_name')\n",
        "```\n",
        "\n",
        "**注意事项**：\n",
        "- 必须在导入 `pyplot` 之前设置\n",
        "- 一旦设置，在当前会话中无法更改\n",
        "- 某些 Backend 需要安装额外的依赖\n",
        "\n",
        "**适用场景**：\n",
        "- 需要特定输出格式\n",
        "- 在不同环境中运行代码\n",
        "- 优化性能或功能需求\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 交互式 Backend vs 非交互式 Backend\n",
        "\n",
        "**交互式 Backend**：\n",
        "- 特点：支持图形窗口，可以缩放、平移、保存\n",
        "- 适用：桌面应用、交互式数据分析\n",
        "- 性能：相对较慢，需要 GUI 支持\n",
        "\n",
        "**非交互式 Backend**：\n",
        "- 特点：只支持文件输出，无图形窗口\n",
        "- 适用：服务器环境、批量生成图表\n",
        "- 性能：更快，资源占用更少\n",
        "\n",
        "### Backend 对性能的影响\n",
        "\n",
        "- **交互式 Backend**：适合交互式使用，但性能较低\n",
        "- **非交互式 Backend（Agg）**：性能最高，适合批量生成\n",
        "- **选择合适的 Backend**：根据使用场景选择，平衡功能和性能\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "当前 Backend: module://matplotlib_inline.backend_inline\n",
            "\n",
            "可用的 Backend 列表：\n"
          ]
        },
        {
          "ename": "AttributeError",
          "evalue": "module 'matplotlib.backend_bases' has no attribute 'Backend'",
          "output_type": "error",
          "traceback": [
            "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
            "\u001b[31mAttributeError\u001b[39m                            Traceback (most recent call last)",
            "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 11\u001b[39m\n\u001b[32m      9\u001b[39m \u001b[38;5;66;03m# 查看所有可用的 Backend\u001b[39;00m\n\u001b[32m     10\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m可用的 Backend 列表：\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m11\u001b[39m available_backends = \u001b[43mmatplotlib\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbackend_bases\u001b[49m\u001b[43m.\u001b[49m\u001b[43mBackend\u001b[49m.__subclasses__()\n\u001b[32m     12\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m backend \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28msorted\u001b[39m([b.\u001b[34m__name__\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m b \u001b[38;5;129;01min\u001b[39;00m available_backends]):\n\u001b[32m     13\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33m  - \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mbackend\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n",
            "\u001b[31mAttributeError\u001b[39m: module 'matplotlib.backend_bases' has no attribute 'Backend'"
          ]
        }
      ],
      "source": [
        "# 基础示例：查看当前 Backend\n",
        "import matplotlib\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "# 查看当前使用的 Backend\n",
        "current_backend = matplotlib.get_backend()\n",
        "print(f\"当前 Backend: {current_backend}\")\n",
        "\n",
        "# 查看所有可用的 Backend\n",
        "print(\"\\n可用的 Backend 列表：\")\n",
        "available_backends = matplotlib.backend_bases.Backend.__subclasses__()\n",
        "for backend in sorted([b.__name__ for b in available_backends]):\n",
        "    print(f\"  - {backend}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "当前 Backend: Agg\n",
            "图表已保存为 backend_agg_example.png\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "/var/folders/9b/jb3nnnqn60x3dtlll6mcf0000000gp/T/ipykernel_76405/9297729.py:24: UserWarning: Glyph 20351 (\\N{CJK UNIFIED IDEOGRAPH-4F7F}) missing from font(s) DejaVu Sans.\n",
            "  plt.savefig('backend_agg_example.png', dpi=150)\n",
            "/var/folders/9b/jb3nnnqn60x3dtlll6mcf0000000gp/T/ipykernel_76405/9297729.py:24: UserWarning: Glyph 29992 (\\N{CJK UNIFIED IDEOGRAPH-7528}) missing from font(s) DejaVu Sans.\n",
            "  plt.savefig('backend_agg_example.png', dpi=150)\n",
            "/var/folders/9b/jb3nnnqn60x3dtlll6mcf0000000gp/T/ipykernel_76405/9297729.py:24: UserWarning: Glyph 32472 (\\N{CJK UNIFIED IDEOGRAPH-7ED8}) missing from font(s) DejaVu Sans.\n",
            "  plt.savefig('backend_agg_example.png', dpi=150)\n",
            "/var/folders/9b/jb3nnnqn60x3dtlll6mcf0000000gp/T/ipykernel_76405/9297729.py:24: UserWarning: Glyph 21046 (\\N{CJK UNIFIED IDEOGRAPH-5236}) missing from font(s) DejaVu Sans.\n",
            "  plt.savefig('backend_agg_example.png', dpi=150)\n",
            "/var/folders/9b/jb3nnnqn60x3dtlll6mcf0000000gp/T/ipykernel_76405/9297729.py:24: UserWarning: Glyph 30340 (\\N{CJK UNIFIED IDEOGRAPH-7684}) missing from font(s) DejaVu Sans.\n",
            "  plt.savefig('backend_agg_example.png', dpi=150)\n",
            "/var/folders/9b/jb3nnnqn60x3dtlll6mcf0000000gp/T/ipykernel_76405/9297729.py:24: UserWarning: Glyph 22270 (\\N{CJK UNIFIED IDEOGRAPH-56FE}) missing from font(s) DejaVu Sans.\n",
            "  plt.savefig('backend_agg_example.png', dpi=150)\n",
            "/var/folders/9b/jb3nnnqn60x3dtlll6mcf0000000gp/T/ipykernel_76405/9297729.py:24: UserWarning: Glyph 34920 (\\N{CJK UNIFIED IDEOGRAPH-8868}) missing from font(s) DejaVu Sans.\n",
            "  plt.savefig('backend_agg_example.png', dpi=150)\n"
          ]
        }
      ],
      "source": [
        "# 基础示例：设置 Backend（必须在导入 pyplot 之前）\n",
        "import matplotlib\n",
        "# 注意：必须在导入 pyplot 之前设置\n",
        "matplotlib.use('Agg')  # 使用非交互式 Backend\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "# 验证 Backend\n",
        "print(f\"当前 Backend: {matplotlib.get_backend()}\")\n",
        "\n",
        "# 绘制图表（不会显示窗口，只能保存）\n",
        "x = np.linspace(0, 10, 100)\n",
        "y = np.sin(x)\n",
        "\n",
        "plt.figure(figsize=(8, 5))\n",
        "plt.plot(x, y, linewidth=2)\n",
        "plt.xlabel('X')\n",
        "plt.ylabel('Y')\n",
        "plt.title('使用 Agg Backend 绘制的图表')\n",
        "plt.grid(True, alpha=0.3)\n",
        "\n",
        "# 保存图表\n",
        "plt.savefig('backend_agg_example.png', dpi=150)\n",
        "print(\"图表已保存为 backend_agg_example.png\")\n",
        "plt.close()  # 关闭图形以释放内存\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "已保存为 PDF 格式\n",
            "已保存为 SVG 格式\n"
          ]
        }
      ],
      "source": [
        "# 进阶示例：不同 Backend 的输出格式\n",
        "import matplotlib\n",
        "import numpy as np\n",
        "\n",
        "# 准备数据\n",
        "x = np.linspace(0, 10, 100)\n",
        "y = np.sin(x)\n",
        "\n",
        "# 示例1: 使用 PDF Backend\n",
        "matplotlib.use('PDF')\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.figure(figsize=(8, 5))\n",
        "plt.plot(x, y, linewidth=2)\n",
        "plt.title('PDF Format')\n",
        "plt.savefig('example.pdf')\n",
        "plt.close()\n",
        "print(\"已保存为 PDF 格式\")\n",
        "\n",
        "# 示例2: 使用 SVG Backend\n",
        "matplotlib.use('SVG')\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.figure(figsize=(8, 5))\n",
        "plt.plot(x, y, linewidth=2)\n",
        "plt.title('SVG Format')\n",
        "plt.savefig('example.svg')\n",
        "plt.close()\n",
        "print(\"已保存为 SVG 格式\")\n",
        "\n",
        "# 注意：在 Jupyter Notebook 中，Backend 设置可能不会按预期工作\n",
        "# 因为 Notebook 会自动管理 Backend\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 实际应用场景：根据环境自动选择 Backend\n",
        "import matplotlib\n",
        "import os\n",
        "import sys\n",
        "\n",
        "def setup_backend():\n",
        "    \"\"\"根据运行环境自动选择合适的 Backend\"\"\"\n",
        "    # 检查是否在 Jupyter Notebook 中\n",
        "    if 'ipykernel' in sys.modules:\n",
        "        # 在 Jupyter 中，通常使用 inline backend\n",
        "        print(\"检测到 Jupyter 环境，使用 inline backend\")\n",
        "        return 'inline'\n",
        "    \n",
        "    # 检查是否有显示设备（桌面环境）\n",
        "    elif os.environ.get('DISPLAY') or sys.platform == 'win32' or sys.platform == 'darwin':\n",
        "        # 有 GUI 环境，尝试使用交互式 Backend\n",
        "        try:\n",
        "            # 尝试使用 TkAgg（最通用的交互式 Backend）\n",
        "            matplotlib.use('TkAgg')\n",
        "            print(\"使用 TkAgg Backend（交互式）\")\n",
        "            return 'TkAgg'\n",
        "        except:\n",
        "            # 如果失败，使用 Agg\n",
        "            matplotlib.use('Agg')\n",
        "            print(\"TkAgg 不可用，使用 Agg Backend（非交互式）\")\n",
        "            return 'Agg'\n",
        "    else:\n",
        "        # 无 GUI 环境（如服务器），使用非交互式 Backend\n",
        "        matplotlib.use('Agg')\n",
        "        print(\"无 GUI 环境，使用 Agg Backend（非交互式）\")\n",
        "        return 'Agg'\n",
        "\n",
        "# 设置 Backend（必须在导入 pyplot 之前）\n",
        "backend = setup_backend()\n",
        "\n",
        "# 现在可以导入 pyplot\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "# 绘制测试图表\n",
        "x = np.linspace(0, 10, 100)\n",
        "y = np.sin(x)\n",
        "\n",
        "plt.figure(figsize=(8, 5))\n",
        "plt.plot(x, y, linewidth=2)\n",
        "plt.title(f'当前 Backend: {matplotlib.get_backend()}')\n",
        "plt.xlabel('X')\n",
        "plt.ylabel('Y')\n",
        "plt.grid(True, alpha=0.3)\n",
        "\n",
        "if backend == 'Agg':\n",
        "    # 非交互式 Backend，只能保存\n",
        "    plt.savefig('auto_backend_example.png', dpi=150)\n",
        "    print(\"图表已保存（非交互式 Backend）\")\n",
        "    plt.close()\n",
        "else:\n",
        "    # 交互式 Backend，可以显示\n",
        "    plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 常见错误和注意事项\n",
        "\n",
        "1. **设置时机错误**\n",
        "   - 错误：在导入 `pyplot` 之后设置 Backend\n",
        "   - 解决：必须在 `import matplotlib.pyplot` 之前设置\n",
        "\n",
        "2. **Backend 不可用**\n",
        "   - 错误：指定的 Backend 未安装或不可用\n",
        "   - 解决：检查依赖是否安装，或使用默认 Backend\n",
        "\n",
        "3. **Jupyter 中的 Backend**\n",
        "   - 在 Jupyter Notebook 中，Backend 由 magic 命令管理\n",
        "   - `%matplotlib inline` 会自动设置 Backend\n",
        "   - 手动设置可能无效\n",
        "\n",
        "4. **交互式 Backend 在服务器上**\n",
        "   - 错误：在无 GUI 的服务器上使用交互式 Backend\n",
        "   - 解决：使用 `Agg` 等非交互式 Backend\n",
        "\n",
        "5. **Backend 切换**\n",
        "   - 一旦设置，在当前 Python 会话中无法更改\n",
        "   - 需要重启 Python 内核才能切换\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 总结\n",
        "\n",
        "### Backend 对比\n",
        "\n",
        "| Backend | 类型 | 特点 | 适用场景 |\n",
        "|---------|------|------|----------|\n",
        "| TkAgg | 交互式 | 跨平台，默认选择 | 桌面应用 |\n",
        "| Qt5Agg | 交互式 | 功能强大 | 需要高级交互 |\n",
        "| Agg | 非交互式 | 高性能，高质量 | 服务器、批量生成 |\n",
        "| PDF | 非交互式 | 矢量格式 | 报告生成 |\n",
        "| SVG | 非交互式 | 矢量格式，可编辑 | Web 应用 |\n",
        "| inline | Jupyter | 内联显示 | Jupyter Notebook |\n",
        "\n",
        "### 最佳实践\n",
        "\n",
        "1. **默认选择**：\n",
        "   - 大多数情况下，使用默认 Backend 即可\n",
        "   - Matplotlib 会自动选择最合适的 Backend\n",
        "\n",
        "2. **服务器环境**：\n",
        "   - 使用 `Agg` Backend，性能好且不需要 GUI\n",
        "\n",
        "3. **特定格式输出**：\n",
        "   - 需要 PDF 时使用 `PDF` Backend\n",
        "   - 需要 SVG 时使用 `SVG` Backend\n",
        "\n",
        "4. **Jupyter Notebook**：\n",
        "   - 使用 `%matplotlib inline`，让 Notebook 自动管理\n",
        "\n",
        "### 性能建议\n",
        "\n",
        "- **批量生成图表**：使用 `Agg` Backend，性能最佳\n",
        "- **交互式使用**：使用 `TkAgg` 或 `Qt5Agg`\n",
        "- **内存管理**：使用非交互式 Backend 时，及时调用 `plt.close()` 释放内存\n",
        "- **避免频繁切换**：在一个会话中保持使用同一个 Backend\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "ml311",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
